home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 1.iso
/
ARGONET
/
PD
/
SOUND
/
REPLAYER.SPK
/
c
/
header
< prev
next >
Wrap
Text File
|
1998-09-04
|
12KB
|
361 lines
/* header.c
Replayer -- audio player
Copyright (c) 1997 Mark Seaborn <mseaborn@argonet.co.uk>
This file provides the functions for reading in Replay file headers.
It should all be portable.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "replayer.h"
/* Declarations */
static int split_soundtrack_line(char *line, char ***tracks);
static int cache_header_info(const replayer_file *x_obj);
static int read_catalogue_value(FILE *file, char end);
/* Public functions for reading Replay headers. */
/* Returns the structure for the Replay header, making sure it has been
loaded first. Returns zero for failure.
*/
const replayer_header *replayer_get_header(const replayer_file *obj)
{
if(!obj) return 0;
cache_header_info(obj);
return obj->header;
}
/* Returns the chunk catalogue for this file, ensuring it is loaded first.
Returns zero for failure.
*/
const replayer_chunk_entry *replayer_get_chunk_catalogue(
const replayer_file *obj)
{
if(!obj) return 0;
cache_header_info(obj);
return obj->chunks;
}
/* Private functions for reading Replay headers. */
/* Generic line-reader function; better than fgets() as there is no
fixed-length buffer. Returns 0 for failure, otherwise the line in
question (you must free it yourself). Used by cache_header_info().
*/
char *replayer_read_header_line(FILE *file)
{
int size = 20;
char *line = malloc(size+1), *p = line;
if(!line) return 0;
while(1) {
int c = fgetc(file);
if(c == EOF || c == '\n') break;
*p++ = c;
/* Allocate some more buffer space if necessary. */
if((p - line + 2) >= size) {
char *old_ptr = line;
size += 20;
line = realloc(line, size+1);
if(!line) { return 0; }
p = p - old_ptr + line;
}
}
*p = 0;
/* Shrink the buffer to minimum size before returning it. */
return realloc(line, strlen(line)+1);
}
int replayer_read_header_number(FILE *file)
{
char *line = replayer_read_header_line(file);
if(line) {
int number = atoi(line);
free(line);
return number;
}
else return 0;
}
/* This function parses header lines containing information about the Replay
file's soundtracks. The sections are separated by |x, where x is the
soundtrack number. Used by cache_header_info(). Returns -1 for failure.
You must free the array used yourself.
*/
static int split_soundtrack_line(char *line, char ***tracks)
{
/* i is the current track being written to;
max is the number of tracks in total. */
int i = 0, max = 1;
*tracks = malloc(sizeof(char*));
if(!*tracks) return -1;
do {
char *next;
(*tracks)[i] = line;
next = strchr(line, '|');
if(next) {
*next = 0;
i = atoi(next+1) - 1; /* Set i to the next track number. */
if(i >= max) max = i+1; /* Update max if necessary. */
*tracks = realloc(*tracks, sizeof(char*) * max);
if(!*tracks) return -1;
line = next + 2;
while(*line == ' ' || *line == '\t') ++line; /* Skip whitespace. */
continue;
}
} while(0);
return max;
}
/* This functions reads an integer in the chunk catalogue from a file until
it reaches the given terminating character. Used by cache_header_info().
Returns -1 for failure.
*/
static int read_catalogue_value(FILE *file, char end)
{
char buffer[20], *s = buffer;
while(1) {
int ch = fgetc(file);
while(ch == ' ') { ch = fgetc(file); } /* Skip any whitespace */
if(ch == end || ch == '\n') break; /* End loop */
if(ch == EOF) return -1; /* Failure */
*s++ = ch;
if(s >= buffer+sizeof(buffer)) return -1;
}
*s = 0;
return atoi(buffer);
}
/* Loads the header information from a Replay file; does nothing if it
hasn't been loaded already. Returns 0 for a failure, !=0 for success.
*/
static int cache_header_info(const replayer_file *x_obj)
{
/* The object passed is const from the client's point of view, because
nothing changes for it. However, we cast it because we've got to
add in the header if it's not loaded already. */
replayer_file *obj = (replayer_file *) x_obj;
int success = 0;
/* If the header is loaded, so should the chunk catalogue,
and vice-versa. */
if(!obj->header || !obj->chunks) {
replayer_header *header = 0;
replayer_soundtrack_info *tracks = 0;
replayer_chunk_entry *chunks = 0;
FILE *file = 0;
char *line;
/* Allocate some space to store the header info. */
header = malloc(sizeof(replayer_header));
if(!header) goto fail;
/* Although we're reading textual bits of the file, it's probably
safer to open it as binary. */
file = fopen(obj->filename, "rb");
if(!file) goto fail;
/* Skip the "ARMovie" line. */
while(1) {
int c = fgetc(file);
if(c == EOF || c == '\n') break;
}
/* Read misc data. */
header->title = replayer_read_header_line(file);
header->date_and_copyright = replayer_read_header_line(file);
header->other_details = replayer_read_header_line(file);
/* Read video information. */
header->video_type = replayer_read_header_line(file);
header->width = replayer_read_header_number(file);
header->height = replayer_read_header_number(file);
header->colour_depth = replayer_read_header_number(file);
header->fps = replayer_read_header_number(file);
/* Read in the settings for each soundtrack.
This is slightly hairy because of the way the settings are
split across each line. */
{
/* Declare the variables we need. */
char **split;
int no_tracks, i;
/* Read in the first line (sound type) and split it. */
line = replayer_read_header_line(file);
if(!line) goto fail;
no_tracks = split_soundtrack_line(line, &split);
if(no_tracks == -1) goto fail;
/* There are no soundtracks if a sound type of 0 is given. */
if(!atoi(split[0])) no_tracks = 0;
/* Allocate the structures to store all the sound settings. */
tracks = malloc(sizeof(replayer_soundtrack_info) * no_tracks);
if(!tracks) goto fail;
header->no_soundtracks = no_tracks;
header->soundtracks = tracks;
/* Read in the sound type values. */
for(i = 0; i < no_tracks; ++i) {
char *copied;
tracks[i].type_number = atoi(split[i]);
if(tracks[i].type_number == 2) {
/* If this is a type 2 track, copy the driver leafname only. */
const char *from = split[i], *to;
while(*from != ' ' && *from) ++from;
while(*from == ' ') ++from;
to = from;
while(*to != ' ' && *to) ++to;
/* Allocate the storage to copy it. */
copied = malloc(to - from + 1);
if(!copied) goto fail;
/* should free other stuff here on failure too */
memcpy(copied, from, to - from);
copied[to - from] = 0;
}
else {
/* Otherwise, for type 1 tracks, copy the whole line. */
copied = malloc(strlen(split[i]) + 1);
if(!copied) goto fail;
/* other stuff should be freed here */
strcpy(copied, split[i]);
}
tracks[i].type = copied;
}
free(split);
free(line);
/* Read the sample rate values. */
line = replayer_read_header_line(file);
if(!line) goto fail;
if(split_soundtrack_line(line, &split) == -1) goto fail;
for(i = 0; i < no_tracks; ++i) {
double rate = atof(split[i]);
if(rate < 256 && rate > 0)
rate = 1e6 / rate; /* Convert usec -> Hz. */
tracks[i].rate = rate;
}
free(split);
free(line);
/* Read the numbers of sound channels (ie. stereo or mono)
and whether they are reversed or not. */
line = replayer_read_header_line(file);
if(!line) goto fail;
if(split_soundtrack_line(line, &split) == -1) goto fail;
for(i = 0; i < no_tracks; ++i) {
char *c;
tracks[i].channels = atoi(split[i]);
/* See below for bit about substrings in header. */
for(c=split[i]; *c; ++c) *c = tolower(*c);
tracks[i].reverse_channels = strstr(split[i], "rever") ? 1:0;
}
free(split);
free(line);
/* Read the sound precision (ie. 8 or 16 bits), and whether the
sound is linear or unsigned, or whatever. */
line = replayer_read_header_line(file);
if(!line) goto fail;
if(split_soundtrack_line(line, &split) == -1) goto fail;
for(i = 0; i < no_tracks; ++i) {
char *c;
tracks[i].precision = atoi(split[i]);
/* The header format uses the system of looking for substrings
in this line to determine some sound format details. However,
the ARMovie documentation disagrees about what the substrings
should be. eg. "AE7doc" says "unsigned", whereas the example
in "ToUseSound" says "unsign". To play it safe, I will use
the short versions, and be case insensitive. */
for(c=split[i]; *c; ++c) *c = tolower(*c);
tracks[i].linear_sound = strstr(split[i], "lin") ? 1:0;
tracks[i].unsigned_sound = strstr(split[i], "unsign") ? 1:0;
tracks[i].adpcm_sound = strstr(split[i], "adpcm") ? 1:0;
/* See the "reversed" substring above as well. */
}
free(split);
free(line);
}
/* Read the rest of the header. */
header->frames_per_chunk = replayer_read_header_number(file);
header->no_chunks = replayer_read_header_number(file);
header->even_chunk_size = replayer_read_header_number(file);
header->odd_chunk_size = replayer_read_header_number(file);
header->chunk_catalogue = replayer_read_header_number(file);
header->sprite_offset = replayer_read_header_number(file);
header->sprite_size = replayer_read_header_number(file);
header->key_frames_offset = replayer_read_header_number(file);
/* Read in the chunk catalogue. This must be done last because we
have to just to a different place in the file to do it.
Remember, the number of chunks given in the header is one less
than in the catalogue. */
{
replayer_chunk_entry *c, *end;
int i, ch;
chunks = malloc(replayer_chunk_entry_SIZE(header->no_soundtracks) *
(header->no_chunks + 1));
if(!chunks) goto fail;
c = chunks;
end = (replayer_chunk_entry *) ((char *) chunks +
replayer_chunk_entry_SIZE(header->no_soundtracks) *
(header->no_chunks + 1));
/* Loop for each catalogue chunk entry. */
if(fseek(file, header->chunk_catalogue, SEEK_SET)) goto fail;
while(c < end) {
c->offset = read_catalogue_value(file, ',');
c->video_size = read_catalogue_value(file, ';');
for(i=0; i<header->no_soundtracks; ++i) {
c->sound_size[i] = read_catalogue_value(file, '|');
if(i == header->no_soundtracks-1) break;
/* Skip the number that follows the '|' until we
reach whitespace, for all but last soundtrack. */
do { ch = fgetc(file); } while(ch != EOF && ch != ' ');
}
c = (replayer_chunk_entry *) ((char *) c +
replayer_chunk_entry_SIZE(header->no_soundtracks));
}
}
/* Finish successfully. */
obj->header = header;
obj->chunks = chunks;
success = 1;
goto finish;
/* Finish after a failure. */
fail:
free(chunks);
free(tracks);
free(header);
/* Drop through to finish. */
finish:
/* Tidy up (possibly after failure). */
if(file) fclose(file);
return success;
}
else return 1; /* Success */
}